﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Drawing;
using ITAP.ShprossesBuilder.Modelling;
using System.Windows.Forms;
using ITAP.ShprossesBuilder.Modelling.Components;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling;

namespace ITAP.ShprossesBuilder.Manipulation
{
    public enum MouseMoveType
    {
        MouseDown,
        MouseMove,
        MouseUp
    }

    public enum MoveDirection
    {
        Horizontal, 
        Vertical,
        Both
    }

    public class MouseManipulatorManager
    {
        public void EnableDisableCommand(Type type, bool enabled)
        {
            foreach (IModelComponentCommand command in commandEventPairs.Commands)
                if (command.GetType() == type)
                    command.Enabled = enabled;
        }

        private MoveDirection moveDirection = MoveDirection.Both;

        public MoveDirection MoveDirection
        {
            get { return moveDirection; }
            set { moveDirection = value; }
        }

        private CommandEventPairs commandEventPairs;
        private CommandHotKeyPairs commandHotKeyPairs;
        private ModelManipulator manipulator;
        private MainControl mainControl;

        public MouseManipulatorManager(MainControl mainControl, ModelManipulator manipulator)
        {
            this.commandEventPairs = new CommandEventPairs();
            this.commandHotKeyPairs = new CommandHotKeyPairs();
            this.mainControl = mainControl;
            this.manipulator = manipulator;
        }

        public void RegisterCommand(IModelComponentCommand command, Keys controlKey, Keys key)
        {
            this.commandHotKeyPairs.Add(command, controlKey, key);
        }

        public void RegisterCommand(IModelComponentCommand command, MouseManagerEvent mouseEvent)
        {
            commandEventPairs.Add(command, mouseEvent);
        }

        public void ClearCommands()
        {
            commandEventPairs.Clear();
        }

        private Point _mouseDownPoint;

        public bool MouseDown(Point mouseDownPoint, MouseManagerEvent mouseEvent)
        {
            List<IModelComponentCommand> commandsToExecute = commandEventPairs.GetCommandsByEvent(mouseEvent);
            foreach (IModelComponentCommand commandToExecute in commandsToExecute)
            {
                commandToExecute.MouseDownPoint = mouseDownPoint;
                this._mouseDownPoint = mouseDownPoint;
                commandToExecute.MouseEvent = mouseEvent;
                commandToExecute.MouseEvent.MouseMoveType = MouseMoveType.MouseDown;
                manipulator.StartExecute(commandToExecute);
            }
            if (commandsToExecute.Count != 0)
                return true;
            else
                return false;
        }

        public bool MouseMove(Point mouseMovePoint, MouseManagerEvent mouseEvent)
        {
            List<IModelComponentCommand> commandsToExecute = commandEventPairs.GetCommandsByEvent(mouseEvent);

            foreach (IModelComponentCommand commandToExecute in commandsToExecute)
            {
                if (!(commandToExecute is CommandEditorNavigation))
                    commandToExecute.MouseMovePoint = this.GetMousePoint(_mouseDownPoint, mouseMovePoint);
                else
                    commandToExecute.MouseMovePoint = mouseMovePoint;

                commandToExecute.MouseEvent = mouseEvent;
                commandToExecute.MouseEvent.MouseMoveType = MouseMoveType.MouseMove;
                manipulator.Execute(commandToExecute);
            }
            if (commandsToExecute.Count != 0)
                return true;
            else
                return false;                    
        }

        public bool MouseUp(Point mouseUpPoint, MouseManagerEvent mouseEvent)
        {
            List<IModelComponentCommand> commandsToExecute = commandEventPairs.GetCommandsByEvent(mouseEvent);
            foreach (IModelComponentCommand commandToExecute in commandsToExecute)
            {
                commandToExecute.MouseUpPoint = this.GetMousePoint(_mouseDownPoint, mouseUpPoint);
                commandToExecute.MouseEvent = mouseEvent;
                commandToExecute.MouseEvent.MouseMoveType = MouseMoveType.MouseUp;
                manipulator.EndExecute(commandToExecute);
            }
            if (commandsToExecute.Count != 0)
            {
                this.UpdateManipulatorVisitors();
                return true;
            }
            else
                return false;
        }

        public bool KeyDown(MouseManagerEvent mouseEvent)
        {
            List<IModelComponentCommand> commandsToExecute = commandEventPairs.GetCommandsByEvent(mouseEvent);
            foreach (IModelComponentCommand commandToExecute in commandsToExecute)
            {
                //if (commandToExecute is CommandOperation)
                {
                    commandToExecute.MouseEvent = mouseEvent;
                    manipulator.StartExecute(commandToExecute);
                    manipulator.Execute(commandToExecute);
                    manipulator.EndExecute(commandToExecute);
                }
                //else
                //{
                //    commandToExecute.MouseEvent = mouseEvent;
                //    manipulator.StartExecute(commandToExecute);
                //}
            }

            if (commandsToExecute.Count != 0)
            {
                this.UpdateManipulatorVisitors();
                return true;
            }
            else
                return false;
            //IModelComponentCommand hotKeyCommand = commandHotKeyPairs.GetCommand(Control.ModifierKeys, mouseEvent);
            //if (hotKeyCommand != null)
            //{
            //    manipulator.StartExecute(hotKeyCommand);
            //    manipulator.Execute(hotKeyCommand);
            //    manipulator.EndExecute(hotKeyCommand);
            //}

        }

        private void UpdateManipulatorVisitors()
        {
            this.manipulator.UpdateAllVisitors();
        }

        private PointF ModelToScreen(ModelPoint point)
        {
            RectangleF bbox = mainControl.ModelControl.ModelContainer.BoundingBox;
            PointF screenPoint = mainControl.ModelControl.CoordinateConverter.ModelPointToScreenPoint(point, mainControl.ModelControl.ClientRectangle.Size.Width, mainControl.ModelControl.ClientRectangle.Size.Height, bbox, mainControl.ModelControl.Camera);
            return screenPoint;
        }

        private ModelPoint ScreenToModel(Point point)
        {
            RectangleF bbox = mainControl.ModelControl.ModelContainer.BoundingBox;
            ModelPoint modelPoint = mainControl.ModelControl.CoordinateConverter.ScreenPointToModelPoint(point, mainControl.ModelControl.ClientRectangle.Size.Width, mainControl.ModelControl.ClientRectangle.Size.Height, bbox, mainControl.ModelControl.Camera);
            return modelPoint;
        }

        private Point GetMousePoint(Point mouseDownPoint, Point mouseMovePoint)
        {
            switch (moveDirection)
            {
                case MoveDirection.Horizontal:
                    return new Point(mouseMovePoint.X, mouseDownPoint.Y);
                case MoveDirection.Vertical:
                    return new Point(mouseDownPoint.X, mouseMovePoint.Y);
                case MoveDirection.Both:
                    return new Point(mouseMovePoint.X, mouseMovePoint.Y);
            }
            return mouseMovePoint;
        }

    }
}
